// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: leon

#pragma once

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "coin2/exchange/base/symbology/amm_product.h"
#include "coin2/exchange/base/symbology/util.h"
#include "coin2/exchange/uniswap/symbology/currency.h"

namespace coin2::exchange::uniswap::symbology {

class UniswapProduct : public base::symbology::AmmProduct<UniswapCurrency, UniswapProduct>,
                       public base::symbology::ProductFromStrMixin<UniswapProduct> {
 public:
  UniswapProduct(
      const UniswapCurrency& base,
      const UniswapCurrency& quote,
      int32_t fee_tier,
      bool omit_fee)
      : AmmProduct(
            base,
            quote,
            fee_tier,
            ToUniswapNativeProduct(base, quote, fee_tier, omit_fee),
            ToUniswapNativeProduct(base, quote, fee_tier, omit_fee),
            base::market::ExchangeType::Uniswap,
            omit_fee) {}

 public:
  static std::unique_ptr<UniswapProduct> FromNativeStrImpl(
      const std::string& native_product,
      int64_t resolve_timestamp) {
    std::unique_ptr<UniswapCurrency> base_ptr;
    std::unique_ptr<UniswapCurrency> quote_ptr;
    int32_t fee_tier;
    bool omit_fee;
    bool success = ParseUniswapNativeProduct(
        native_product, resolve_timestamp,
        &base_ptr, &quote_ptr, &fee_tier, &omit_fee);
    if (success) {
      return std::make_unique<UniswapProduct>(
          *base_ptr, *quote_ptr, fee_tier, omit_fee);
    } else {
      return {};
    }
  }

 private:
  static bool ParseUniswapNativeProduct(
      const std::string& product_str,
      int64_t resolve_timestamp,
      std::unique_ptr<UniswapCurrency>* base_ptr,
      std::unique_ptr<UniswapCurrency>* quote_ptr,
      int32_t* fee_tier,
      bool* omit_fee) {
    std::vector<std::string> splitted;
    splitted = Split(product_str, "-.");
    if (splitted.size() < 2) {
      return false;
    }
    *base_ptr = UniswapCurrency::FromNativeStrNoThrow(splitted[0], resolve_timestamp);
    *quote_ptr = UniswapCurrency::FromNativeStrNoThrow(splitted[1], resolve_timestamp);
    *fee_tier = 30;
    *omit_fee = true;
    if (splitted.size() == 3) {
      std::size_t pos{};
      *fee_tier = std::stoi(splitted[2], &pos);
      CHECK_EQ(splitted[2].substr(pos), "bp");
      *omit_fee = false;
    }
    return true;
  }

  static std::string ToUniswapNativeProduct(
      const UniswapCurrency& base,
      const UniswapCurrency& quote,
      int32_t fee_tier,
      bool omit_fee) {
    using base::market::MarketType;
    std::stringstream ss;
    ss << base.native_currency(MarketType::Spot) << '-'
       << quote.native_currency(MarketType::Spot);
    if (!omit_fee) {
       ss << "." << fee_tier << "bp";
    }
    return ss.str();
  }
};
}  // namespace coin2::exchange::uniswap::symbology
